home *** CD-ROM | disk | FTP | other *** search
/ EnigmA Amiga Run 1995 November / EnigmA AMIGA RUN 02 (1995)(G.R. Edizioni)(IT)[!][issue 1995-11][Skylink CD].iso / earcd / unix / mp14tar.z / mp14tar / mpack / uudecode.c < prev    next >
C/C++ Source or Header  |  1994-06-01  |  11KB  |  444 lines

  1. /* (C) Copyright 1993 by John G. Myers
  2.  * All Rights Reserved.
  3.  *
  4.  * Permission to use, copy, modify, distribute, and sell this software
  5.  * and its documentation for any purpose is hereby granted without
  6.  * fee, provided that the above copyright notice appear in all copies
  7.  * and that both that copyright notice and this permission notice
  8.  * appear in supporting documentation, and that the name of John G.
  9.  * Myers not be used in advertising or publicity pertaining to
  10.  * distribution of the software without specific, written prior
  11.  * permission.  John G. Myers makes no representations about the
  12.  * suitability of this software for any purpose.  It is provided "as
  13.  * is" without express or implied warranty.
  14.  *
  15.  * JOHN G. MYERS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
  16.  * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
  17.  * FITNESS, IN NO EVENT SHALL JOHN G. MYERS BE LIABLE FOR ANY SPECIAL,
  18.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
  19.  * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
  20.  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
  21.  * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  22.  */
  23. #include <stdio.h>
  24. #include <ctype.h>
  25. #include <string.h>
  26. #include "xmalloc.h"
  27. #include "common.h"
  28.  
  29. extern char *os_idtodir();
  30. extern FILE *os_newtypedfile();
  31.  
  32. /*
  33.  * Read an input file, looking for data in split-uuencode format
  34.  */
  35. handleUuencode(infile, subject)
  36. FILE *infile;
  37. char *subject;
  38. {
  39.     char *fname;
  40.     int part = 0, nparts = 0;
  41.     char buf[1024];
  42.     char *p;
  43.  
  44.     if (parseSubject(subject, &fname, &part, &nparts) == 0) {
  45.     return saveUuFile(infile, fname, part, nparts, (char *)0);
  46.     }
  47.  
  48.     /* Scan for something that looks like the start of uuencoded data */
  49.     while (fgets(buf, sizeof(buf), infile)) {
  50.     if (!strncmp(buf, "begin ", 6) &&
  51.         isdigit(buf[6]) && isdigit(buf[7]) && isdigit(buf[8]) &&
  52.         buf[9] == ' ') {
  53.         /* Possible single-part uuencoded file */
  54.         return saveUuFile(infile, (char *)0, 1, 0, buf);
  55.     }
  56.     if (!strncmp(buf, "section ", 8) &&
  57.         isdigit(buf[8])) {
  58.         /* Might be "section N of ... of file F ..." */
  59.         part = 0;
  60.         for (p = buf+8; isdigit(*p); p++) part = part*10 + *p - '0';
  61.         if (strncmp(p, " of ", 4) != 0) continue;
  62.         for (p += 4; *p && strncmp(p, " of file ", 9) != 0; p++);
  63.         if (!*p) continue;
  64.         p += 9;
  65.         fname = p;
  66.         p = strchr(p, ' ');
  67.         if (!p) continue;
  68.         *p = '\0';
  69.         return saveUuFile(infile, fname, part, 0, (char *)0);
  70.     }
  71.     if (!strcmp(buf, "(This file must be converted with BinHex 4.0)\n")) {
  72.         return os_binhex(infile, 1, 1);
  73.     }
  74.     }    
  75.  
  76.     return 0;
  77. }
  78.  
  79. /*
  80.  * Handle a split-uuencode part
  81.  * If nparts is 0, then look for an "end" line to detect the last part.
  82.  * If fname is null, then we are attempting to decode a single-part message.
  83.  * If firstline is non-null, it is written as the first line of the saved part
  84.  */
  85. int
  86. saveUuFile(infile, fname, part, nparts, firstline)
  87. FILE *infile;
  88. char *fname;
  89. int part;
  90. int nparts;
  91. char *firstline;
  92. {
  93.     char buf[1024];
  94.     char *dir;
  95.     FILE *partfile;
  96.  
  97.     if (fname) {
  98.     sprintf(buf, "Saving %s part %d", fname, part);
  99.     if (nparts) sprintf(buf+strlen(buf), " of %d", nparts);
  100.     chat(buf);
  101.     }
  102.     else fname = "unknown";
  103.  
  104.     /* Create directory to store parts and copy this part there. */
  105.     dir = os_idtodir(fname);
  106.     if (!dir) return 1;
  107.     sprintf(buf, "%s%d", dir, part);
  108.     partfile = fopen(buf, "w");
  109.         if (!partfile) {
  110.     os_perror(buf);
  111.     return 1;
  112.     }
  113.     if (firstline) fputs(firstline, partfile);
  114.     while (fgets(buf, sizeof(buf), infile)) {
  115.     fputs(buf, partfile);
  116.     if (nparts == 0 && strcmp(buf, "end\n") == 0) {
  117.         /* This is the last part. Remember the fact */
  118.         nparts = part;
  119.         fclose(partfile);
  120.         sprintf(buf, "%sCT", dir);
  121.         partfile = fopen(buf, "w");
  122.         if (!partfile) {
  123.         os_perror(buf);
  124.         }
  125.         else {
  126.         fprintf(partfile, "%d\n", nparts);
  127.         }
  128.         break;
  129.     }
  130.     }
  131.     fclose(partfile);
  132.  
  133.     /* Retrieve any previously saved number of the last part */
  134.     if (nparts == 0) {
  135.     sprintf(buf, "%sCT", dir);
  136.     if (partfile = fopen(buf, "r")) {
  137.         if (fgets(buf, sizeof(buf), partfile)) {
  138.         nparts = atoi(buf);
  139.         if (nparts < 0) nparts = 0;
  140.         }
  141.         fclose(partfile);
  142.     }
  143.     }
  144.  
  145.     if (nparts == 0) return 0;
  146.  
  147.     /* Check to see if we have all parts.  Start from the highest numbers
  148.      * as we are more likely not to have them.
  149.      */
  150.     for (part = nparts; part; part--) {
  151.     sprintf(buf, "%s%d", dir, part);
  152.     partfile = fopen(buf, "r");
  153.     if (partfile) {
  154.         fclose(partfile);
  155.     }
  156.     else {
  157.         return 0;
  158.     }
  159.     }
  160.  
  161.     return uudecodefiles(dir, nparts);
  162. }
  163.  
  164. /*
  165.  * Parse a Subject: header, looking for clues with which to decode
  166.  * split-uuencoded data.
  167.  */
  168. int
  169. parseSubject(subject, fnamep, partp, npartsp)
  170. char *subject;
  171. char **fnamep;
  172. int *partp;
  173. int *npartsp;
  174. {
  175.     char *scan, *bak;
  176.     int part = 0, nparts = 0;
  177.  
  178.     /* No subject header */
  179.     if (!subject) return 1;
  180.  
  181.     /* Skip leading whitespace and other garbage */
  182.     scan = subject;
  183.     while (isspace(*scan) || *scan == '-') scan++;
  184.     if (!cistrncmp(scan, "repost", 6)) {
  185.     for (scan += 6; isspace(*scan) || *scan == ':' || *scan == '-'; scan++);
  186.     }
  187.  
  188.     /* Replies aren't usually data */
  189.     if (!cistrncmp(scan, "re:", 3)) return 1;
  190.  
  191.     /* Get filename */
  192.     *fnamep = scan;
  193.     while (isalnum(*scan) || *scan == '-' || *scan == '+' || *scan == '&'
  194.        || *scan == '_' || *scan == '.') scan++;
  195.     if (*fnamep == scan || !*scan) return 1;
  196.     *scan++ = '\0';
  197.  
  198.     /* Get part number */
  199.     while (*scan) {
  200.     /* look for "1/6" or "1 / 6" or "1 of 6" or "1o6" */
  201.     if (isdigit(*scan) &&
  202.         (scan[1] == '/'
  203.          || (scan[1] == ' ' && scan[2] == '/')
  204.          || (scan[1] == ' ' && scan[2] == 'o' && scan[3] == 'f')
  205.          || (scan[1] == '-' && scan[2] == 'o' && scan[3] == 'f')
  206.          || (scan[1] == 'o' && isdigit(scan[2])))) {
  207.         while (isdigit(scan[-1])) scan--;
  208.         part = 0;
  209.         while (isdigit(*scan)) {
  210.         part = part * 10 + *scan++ - '0';
  211.         }
  212.         while (scan != '\0' && !isdigit(*scan)) scan++;
  213.         if (isdigit(*scan)) {
  214.         nparts = 0;
  215.         while (isdigit(*scan)) {
  216.             nparts = nparts * 10 + *scan++ - '0';
  217.         }
  218.         }
  219.         break;
  220.     }
  221.  
  222.     /* look for "6 parts" or "part 1" */
  223.     if (!cistrncmp("part", scan, 4)) {
  224.         if (scan[4] == 's') {
  225.         for (bak = scan; bak >= subject && !isdigit(*bak); bak--);
  226.         if (bak > subject) {
  227.             while (bak > subject && isdigit(bak[-1])) bak--;
  228.             nparts = 0;
  229.             while (isdigit(*bak)) {
  230.             nparts = nparts * 10 + *bak++ - '0';
  231.             }
  232.         }
  233.         } else {
  234.         while (*scan != '\0' && !isdigit(*scan)) scan++;
  235.         part = 0;
  236.         while (isdigit(*scan)) {
  237.             part = part * 10 + *scan++ - '0';
  238.         }
  239.         scan -= 2;
  240.         }
  241.     }
  242.     scan++;
  243.     }
  244.  
  245.     if (nparts == 0 || part == 0 || part > nparts) return 1;
  246.     *partp = part;
  247.     *npartsp = nparts;
  248.     return 0;
  249. }
  250.  
  251. /* Length of a normal uuencoded line, including newline */
  252. #define UULENGTH 62
  253.  
  254. /*
  255.  * Decode the uuencoded file that is in 'nparts' pieces in 'dir'.
  256.  */
  257. int
  258. uudecodefiles(dir, nparts)
  259. char *dir;
  260. int nparts;
  261. {
  262.     int part;
  263.     enum {st_start, st_desc, st_inactive, st_decode, st_nextlast, st_last,
  264.         st_binhex} state;
  265.     FILE *infile;
  266.     FILE *outfile;
  267.     char buf[1024];
  268.     char lastline[UULENGTH+1];
  269.     char *fname, *p;
  270.     char *contentType = "application/octet-stream";
  271.     int line_length = 0;
  272.  
  273.     /* Create description filename */
  274.     outfile = fopen(TEMPFILENAME, "w");
  275.     state = outfile ? st_desc : st_start;
  276.  
  277.     /* Handle each part in order */
  278.     for (part = 1; part <= nparts; part++) {
  279.     sprintf(buf, "%s%d", dir, part);
  280.     infile = fopen(buf, "r");
  281.     if (!infile) {
  282.         os_perror(buf);
  283.         if (outfile) fclose(outfile);
  284.         remove(TEMPFILENAME);
  285.         return 1;
  286.     }
  287.  
  288.     while (fgets(buf, sizeof(buf), infile)) {
  289.         switch (state) {
  290.         case st_desc:    /* Copying description text */
  291.         if (!strncmp(buf, "---", 3) ||
  292.             !strncmp(buf, "#!", 2) ||
  293.             !cistrncmp(buf, "part=", 5) ||
  294.             !cistrncmp(buf, "begin", 5)) {
  295.             fclose(outfile);
  296.             outfile = 0;
  297.             state = st_start;
  298.         }
  299.         else {
  300.             fputs(buf, outfile);
  301.             break;
  302.         }
  303.         /* FALL THROUGH */
  304.  
  305.         case st_start:    /* Looking for start of uuencoded
  306.                  *  or binhex'ed file */
  307.         if (!strcmp(buf,
  308.             "(This file must be converted with BinHex 4.0)\n")) {
  309.             state = st_binhex;
  310.             os_binhex(infile, part, nparts);
  311.             goto endbinhex;
  312.         }
  313.         if (strncmp(buf, "begin ", 6)) break;
  314.         /* skip mode */
  315.         p = buf + 6;
  316.         while (*p && !isspace(*p)) p++;
  317.         while (*p && isspace(*p)) p++;
  318.         fname = p;
  319.         while (*p && !isspace(*p)) p++;
  320.         *p = '\0';
  321.         if (!*fname) return 1;
  322.  
  323.         /* Guess the content-type of common filename extensions */
  324.         if (strlen(fname) > 4) {
  325.             p = fname + strlen(fname)-4;
  326.             if (!cistrcmp(p, ".gif")) contentType = "image/gif";
  327.             if (!cistrcmp(p, ".jpg")) contentType = "image/jpeg";
  328.             if (!cistrcmp(p, ".jpeg")) contentType = "image/jpeg";
  329.             if (!cistrcmp(p, ".mpg")) contentType = "video/mpeg";
  330.             if (!cistrcmp(p, ".mpeg")) contentType = "video/mpeg";
  331.         }
  332.  
  333.         /* Create output file and start decoding */
  334.         outfile = os_newtypedfile(fname, contentType, FILE_BINARY);
  335.         if (!outfile) {
  336.             fclose(infile);
  337.             return 1;
  338.         }
  339.         state = st_decode;
  340.         break;
  341.  
  342.         case st_inactive:    /* Looking for uuencoded data to resume */
  343.         if (*buf != 'M' || strlen(buf) != line_length) break;
  344.         state = st_decode;
  345.         /* FALL THROUGH */
  346.         case st_decode:    /* Decoding data */
  347.         if (line_length == 0) line_length = strlen(buf);
  348.         if (*buf == 'M' && strlen(buf) == line_length) {
  349.             uudecodeline(buf, outfile);
  350.             break;
  351.         }
  352.         if (strlen(buf) > line_length) {
  353.             state = st_inactive;
  354.             break;
  355.         }
  356.         /*
  357.          * May be on nearing end of file.
  358.          * Save this line in case we are.
  359.          */
  360.         strcpy(lastline, buf);
  361.         if (*buf == ' ' || *buf == '`') {
  362.             state = st_last;
  363.         }
  364.         else {
  365.             state = st_nextlast;
  366.         }
  367.         break;
  368.  
  369.         case st_nextlast:    /* May be nearing end of file */
  370.         if (*buf == ' ' || *buf == '`') {
  371.             state = st_last;
  372.         }
  373.         else {
  374.             state = st_inactive;
  375.         }
  376.         break;
  377.  
  378.         case st_last:    /* Should be at end of file */
  379.         if (!strcmp(buf, "end\n")) {
  380.             /* Handle that last line we saved */
  381.             uudecodeline(lastline, outfile);
  382.             fclose(infile);
  383.             os_closetypedfile(outfile);
  384.             for (;part <= nparts; part++) {
  385.             sprintf(buf, "%s%d", dir, part);
  386.             remove(buf);
  387.             }
  388.             os_donewithdir(dir);
  389.             return 0;
  390.         }
  391.         state = st_inactive;
  392.         break;
  393.  
  394.         case st_binhex:
  395.         if (strncmp(buf, "---", 3)) break;
  396.         os_binhex(infile, part, nparts);
  397.         goto endbinhex;
  398.         }
  399.     }
  400.     if (state != st_binhex) state = st_inactive;
  401.     endbinhex:
  402.     fclose(infile);
  403.     sprintf(buf, "%s%d", dir, part);
  404.     remove(buf);
  405.     }
  406.     if (outfile) os_closetypedfile(outfile);
  407.     if (state == st_binhex) os_binhex(0, 0, 0);
  408.     sprintf(buf, "%sCT", dir);
  409.     remove(buf);
  410.     os_donewithdir(dir);
  411.     return 0;
  412. }
  413.  
  414. #define DEC(c)    (((c) - ' ') & 077)
  415.  
  416. /*
  417.  * Decode a uuencoded line to 'outfile'
  418.  */
  419. uudecodeline(line, outfile)
  420. char *line;
  421. FILE *outfile;
  422. {
  423.     int c, len;
  424.  
  425.     len = DEC(*line++);
  426.     while (len) {
  427.     c = DEC(*line) << 2 | DEC(line[1]) >> 4;
  428.     putc(c, outfile);
  429.     if (--len) {
  430.         c = DEC(line[1]) << 4 | DEC(line[2]) >> 2;
  431.         putc(c, outfile);
  432.         if (--len) {
  433.         c = DEC(line[2]) << 6 | DEC(line[3]);
  434.         putc(c, outfile);
  435.         len--;
  436.         }
  437.     }
  438.     line += 4;
  439.     }
  440.     return;
  441. }
  442.  
  443.     
  444.